#include "TokenizerInputTests.hpp"
#include "../TokenizerInput.hpp"
#include "../../Shared/Shared.hpp"

#include <iostream>

namespace CPreProc
{
  void TokenizerInputTests::test_simple()
  {
    const char lines[] =
      "#include <stdio.h>\n"
      "\n"
      "int main()\n"
      "{\n"
      "\tprintf(\"Hello, world\");\n"
      "}\n";
    Glib::RefPtr<TokenizerInput> tokenizer_doc = TokenizerInput::create(lines);

    TokenizerInput::LineIterator it = tokenizer_doc->get_line_iterator();

    bool read_res;

    read_res = it.move_next();
    assert(read_res);
    assert(it.to_string() == "#include <stdio.h>");

    read_res = it.move_next();
    assert(read_res);
    assert(it.to_string() == "");

    read_res = it.move_next();
    assert(read_res);
    assert(it.to_string() == "int main()");

    read_res = it.move_next();
    assert(read_res);
    assert(it.to_string() == "{");

    read_res = it.move_next();
    assert(read_res);
    assert(it.to_string() == "\tprintf(\"Hello, world\");");

    read_res = it.move_next();
    assert(read_res);
    assert(it.to_string() == "}");

    read_res = it.move_next();
    assert(read_res);
    assert(it.to_string() == "");

    read_res = it.move_next();
    assert(!read_res);
  }

  void TokenizerInputTests::test_get_begin_and_end()
  {
    char input[] = "one line of code";
    Glib::RefPtr<TokenizerInput> doc = TokenizerInput::create(input);

    TokenizerInput::LineIterator it = doc->get_line_iterator();
    bool read_res = it.move_next();
    assert(read_res == true);

    int i = 0;
    for(const char *c = it.get_line_begin();c != it.get_line_end();c++)
    {
      assert(*c == input[i]);
      i++;
    }
    assert(i == strlen(input));
  }

  void TokenizerInputTests::tests_from_standard_spec()
  {
    // four-character string literal
    simple_single_output_line("\"a//b\"","\"a//b\"");

    simple_single_output_line("f = g/**//h","f = g /h");

    // test a simple two line comment
    simple_single_output_line("//\\\ni();","");

    // // is seperated by a newline, that's removed by a \ token.
    simple_single_output_line("/\\\n/i();","");

    simple_single_output_line("/*//*/ l();","  l();");
    simple_single_output_line("m = n//**/o","m = n");

    // test nested comment blocks
    simple_single_output_line("/* a /* b */ c */ d","  c */ d");

    // test a quote char inside a comment
    simple_single_output_line("/* \" */"," ");
  }

  // fancy stuff, to circumvent the trigraph replacement of the compiler that's 
  // currently compiling this source file.
  static void q_to_question_mark(std::string &s)
  {
    for(int i = 0;i < (int)s.length();i++)
    {
      if(s[i] == 'q')
        s[i] = '?';
    }
  }

  void TokenizerInputTests::test_trigraphs()
  {
    std::string input = "qq= qq( qq/ qq) qq' qq< qq! qq> qq-";
    q_to_question_mark(input);
    simple_single_output_line(input,"# [ \\ ] ^ { | } ~");

    input = "printf(\"Ehqqq/n\");";
    q_to_question_mark(input);
    simple_single_output_line(input,"printf(\"Eh?\\n\");");

    // trigraph used to splice a line
    input = "line 1 qq/\nline 2";
    q_to_question_mark(input);
    simple_single_output_line(input,"line 1 line 2");
  }

  void TokenizerInputTests::simple_single_output_line(const Glib::ustring &input,const Glib::ustring &expected_output)
  {
    Glib::RefPtr<TokenizerInput> doc = TokenizerInput::create(input);

    TokenizerInput::LineIterator it = doc->get_line_iterator();
    bool read_res = it.move_next();
    assert(read_res == true);
    Glib::ustring line_str = it.to_string();
    
    assert(line_str == expected_output);

    // the output should be a single line, so if we try 
    // to read another line, it should return false.
    read_res = it.move_next();
    assert(read_res == false);
  }

  void TokenizerInputTests::test_get_source_location()
  {
    {
      Glib::RefPtr<TokenizerInput> doc = TokenizerInput::create("just a normal line");
      TokenizerInput::LineIterator it = doc->get_line_iterator();
      bool read_res = it.move_next();
      assert(read_res);
      assert(it.to_string() == "just a normal line");
      assert(doc->get_source_location(it.get_line_begin() + 10) == SourceLocation(0,10));
    }
    
    {
      // test with trigraph replacement
      std::string line = "qq=a";
      q_to_question_mark(line);
      Glib::RefPtr<TokenizerInput> doc = TokenizerInput::create(line);
      TokenizerInput::LineIterator it = doc->get_line_iterator();
      bool read_res = it.move_next();
      assert(read_res);
      assert(it.to_string() == "#a");
      assert(doc->get_source_location(it.get_line_begin()) == SourceLocation(0,0));
      assert(doc->get_source_location(it.get_line_begin() + 1) == SourceLocation(0,3));
    }

    {
      const char line[] = "a line /* with a comment */ right?";
      Glib::RefPtr<TokenizerInput> doc = TokenizerInput::create(line);
      TokenizerInput::LineIterator it = doc->get_line_iterator();
      bool read_res = it.move_next();
      assert(read_res);
      assert(it.to_string() == "a line   right?");
      assert(doc->get_source_location(it.get_line_end() - 2) == SourceLocation(0,(int)strlen(line) - 2));
    }
  }
}
